package index

import (
	"bitcask/data"
	"sync"

	"github.com/google/btree"
)

type BTree struct {
	tree *btree.BTree
	lock *sync.RWMutex
}

// NewBTree 创建一个新的B树索引实例
func NewBTree() *BTree {
	return &BTree{
		tree: btree.New(32),
		lock: new(sync.RWMutex),
	}
}

// Put 将键值对插入到B树索引中，如果键已存在则更新其位置信息
//
// 初级解释:
// 这个方法实现了向B树索引中添加或更新一个键值对的操作。
// 它创建一个新的索引项，获取写锁，执行插入/更新操作，然后释放锁。
//
// 进阶说明:
//   - 使用读写锁(RWMutex)而非互斥锁(Mutex)是为了提高并发性能，
//     允许多个读操作同时进行，但写操作需要独占锁
//   - ReplaceOrInsert是Google btree库的核心方法，当键存在时更新，
//     不存在时插入，保证了操作的原子性
//   - 返回bool类型遵循了Go语言中常见的成功/失败标识模式
//
// 实战要点:
//   - 在高并发场景下，锁的竞争可能成为性能瓶颈，可以考虑
//     分片锁(shard lock)或无锁数据结构优化
//   - Item对象的创建会产生少量GC压力，在极高频调用场景下可考虑
//     对象池复用
//   - 未检查key和pos参数的有效性，生产环境中建议增加参数校验
//
// 扩展思考:
// - 相比Java的ConcurrentHashMap，Go通过显式锁管理提供了更细粒度的控制
// - Rust中可能会使用Arc<RwLock<BTreeMap>>配合不可变语义来实现类似功能
// - 可以进一步优化为批量插入接口，减少锁获取/释放的开销
func (bt *BTree) Put(key []byte, pos *data.LogRecordPos) *data.LogRecordPos {
	// 构造索引项，将key和位置信息封装成btree.Item接口实现
	it := &Item{key, pos}

	// 获取写锁，确保并发安全性
	// 注意：此处应使用写锁而非读锁，因为后续操作会修改数据结构
	bt.lock.Lock()

	// 调用btree库的ReplaceOrInsert方法执行实际的插入或替换操作
	// 此方法会根据Item的Less()比较函数确定插入位置
	oldItem := bt.tree.ReplaceOrInsert(it)

	// 释放写锁，允许其他goroutine访问
	bt.lock.Unlock()

	if oldItem == nil {
		return nil
	}
	// 返回操作成功标志，这里始终返回true，实际可根据插入结果优化
	return oldItem.(*Item).pos
}

// Get 根据键查找对应的位置信息
//
// 初级解释:
// 通过给定的键在B树中查找对应的值位置信息，如果找到则返回位置指针，否则返回nil
//
// 进阶说明:
// - 使用读操作不需要获取写锁，体现了读写锁的优势，多个读操作可以并发执行
// - 通过构造临时Item对象进行查找，避免了直接在btree中处理[]byte比较逻辑
// - 类型断言 btreeItem.(*Item) 是Go中接口转换的典型用法
//
// 实战要点:
// - 在高并发读多写少场景下，读写锁能显著提升性能
// - 注意Get操作没有加锁，依赖于btree内部的并发安全机制
// - 返回的是*data.LogRecordPos指针，调用方需要注意生命周期管理
func (bt *BTree) Get(key []byte) *data.LogRecordPos {
	// 构造用于查找的Item对象，只需要key即可，pos字段未使用
	it := &Item{key: key}

	// 调用btree的Get方法查找匹配的项
	// btree内部使用Item.Less()方法进行比较定位
	btreeItem := bt.tree.Get(it)

	// 如果未找到匹配项，返回nil表示键不存在
	if btreeItem == nil {
		return nil
	}

	// 类型断言将btree.Item接口转换回具体的Item结构体
	// 然后返回其中存储的位置信息pos
	return btreeItem.(*Item).pos
}

// Delete 从B树中删除指定键的条目
//
// 初级解释:
// 删除指定键对应的索引项，如果删除成功返回true，如果键不存在返回false
//
// 进阶说明:
// - 删除操作需要写锁保护，确保线程安全
// - Delete方法返回被删除的项，通过判断返回值确定是否真正删除了数据
// - 锁的粒度控制很重要，尽早释放锁可以提高并发性能
//
// 实战要点:
// - 必须先获取写锁再执行删除操作
// - 当Delete返回nil时表示键不存在，此时应立即释放锁并返回失败
// - 方法名为delete(小写)，表明这是一个非导出方法，只能在包内使用
//
// 扩展思考:
// - 相比Rust的Option类型，Go使用nil表示"无值"状态，但缺少编译期检查
// - Java中类似操作通常会返回Boolean.TRUE/FALSE而不是原始boolean
func (bt *BTree) Delete(key []byte) bool {
	// 构造用于删除的Item对象
	it := &Item{key: key}

	// 获取写锁，保护并发删除操作
	bt.lock.Lock()

	// 执行删除操作，返回被删除的项
	oldItem := bt.tree.Delete(it)

	// 如果返回nil说明键不存在，释放锁后返回false
	if oldItem == nil {
		bt.lock.Unlock()
		return false
	}

	// 删除成功，释放锁并返回true
	// 注意：这里可以考虑使用defer unlock来简化锁管理
	bt.lock.Unlock()
	return true
}
